import { push, pop, peek } from './SchedulerMinHeap';
import {
    NoPriority,
    ImmediatePriority,
    UserBlockingPriority,
    NormalPriority,
    LowPriority,
    IdlePriority,
} from './SchedulerPriorities'
function getCurrentTime() {
    return performance.now();
}

var maxSigned31BitInt = 1073741823;

// Times out immediately 立刻过期
var IMMEDIATE_PRIORITY_TIMEOUT = -1;
// Eventually times out
var USER_BLOCKING_PRIORITY_TIMEOUT = 250;
// 正常优先级的过期时间
var NORMAL_PRIORITY_TIMEOUT = 5000;
// 低优先级的过期时间
var LOW_PRIORITY_TIMEOUT = 10000;
// Never times out 永远不过期，只要有别的优先级都能打断，仅在空闲的时候执行的任务
var IDLE_PRIORITY_TIMEOUT = maxSigned31BitInt;

// 任务id计数器，排序用的
let taskIdCounter = 1;
// 任务最小堆
const taskQueue = [];
let scheduleHostCallback = null;
let startTime = -1;
let currentTask = null;
// React每一帧向浏览器申请5ms用于自己任务执行
// 如果5ms内没有完成，React也会放弃控制权，把控制交还给浏览器
const frameInterval = 5;

const channel = new MessageChannel();
var port2 = channel.port2;
var port1 = channel.port1;
port1.onmessage = performWorkUntilDeadline;

/**
 * 按优先级执行任务
 * @date 2023-04-05
 * @param {any} priorityLevel
 * @param {any} callback
 * @returns {any}
 */
export function scheduleCallback(priorityLevel, callback) {
    // 获取当前的时间
    const currentTime = getCurrentTime();
    // 此任务的开始时间
    const startTime = currentTime;
    // 超时时间
    let timeout;
    switch (priorityLevel) {
        case ImmediatePriority:
            timeout = IMMEDIATE_PRIORITY_TIMEOUT; // -1
            break;
        case UserBlockingPriority:
            timeout = USER_BLOCKING_PRIORITY_TIMEOUT; // 250ms
            break;
        case IdlePriority:
            timeout = IDLE_PRIORITY_TIMEOUT; // 1073741823
            break;
        case LowPriority:
            timeout = LOW_PRIORITY_TIMEOUT; // 10s
            break;
        case NormalPriority:
        default:
            timeout = NORMAL_PRIORITY_TIMEOUT; // 5s
            break;
    }
    // 计算此任务的过期时间
    const expirationTime = startTime + timeout;
    const newTask = {
        id: taskIdCounter++,
        callback, // 回调函数或者说是任务函数
        priorityLevel, // 优先级别
        startTime, // 任务的开始时间
        expirationTime, // 任务的过期时间
        sortIndex: expirationTime,  // 排序依赖，这里源码是-1，不过经过一定逻辑处理会改为过期时间
    };
    push(taskQueue, newTask);
    // flushWork就是执行任务
    requestHostCallback(flushWork);
    return newTask;
}

/**
 * 开始执行任务队列中的任务
 * @date 2023-04-05
 * @param {any} startTime
 * @returns {any}
 */
function flushWork(startTime) {
    return workLoop(startTime);
}

function shouldYieldToHost() {
    // 用当前时间减去开始的时间就是过去的时间
    const timeElapsed = getCurrentTime() - startTime;
    // 如果经过的时间小于5ms，则返回false，那就不需要放弃执行
    if (timeElapsed < frameInterval) {
        return false;
    }
    // 5ms用完了，放弃执行
    return true;
}

function workLoop(startTime) {
    let currentTime = startTime;
    // 取出优先级最高的任务，任务队列因为是最小堆，已经排好序，所以最顶的任务就是优先级最高的任务
    currentTask = peek(taskQueue);
    while (currentTask !== null) {
        // 如果此任务的过期时间小于当前时间，也就是说没有过期，并且需要放弃执行(时间片到期了)
        if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
            // 跳出工作循环
            break;
        }
        // 取出当前的任务中的回调函数 performConcurrentWorkOnRoot
        const callback = currentTask.callback;
        if (typeof callback === 'function') {
            currentTask.callback = null;
            const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
            // 如果执行工作后还返回了新的函数，表示当前工作未完成
            const continuationCallback = callback(didUserCallbackTimeout);
            if (typeof continuationCallback === 'function') {
                currentTask.callback = continuationCallback;
                return true; // 还有任务要执行
            }
            // 如果此任务已经完成，则弹出此任务
            if (currentTask === peek(taskQueue)) {
                pop(taskQueue);
            }
        } else {
            pop(taskQueue);
        }
        // 如果当前任务执行完成或者不合法，执行下一个任务
        currentTask = peek(taskQueue);
    }
    // 如果循环结束，还有未完成的任务，那就标示hasMoreTask = true
    if (currentTask !== null) return true;
    // 没有任何要完成的任务
    return false;
}

function requestHostCallback(flushWork) {
    // 先缓存回调函数
    scheduleHostCallback = flushWork;
    // 执行工作直到截止时间
    schedulePerformWorkUntilDeadline();
}

function schedulePerformWorkUntilDeadline() {
    port2.postMessage(null);
}

function performWorkUntilDeadline() {
    if (scheduleHostCallback) {
        startTime = getCurrentTime();
        let hasMoreWork = true;
        try {
            // 执行flushWork，并判断有没返回值
            hasMoreWork = scheduleHostCallback(startTime);
        } finally {
            // 如果执行完之后为true，说明还有工作要继续做
            if (hasMoreWork) {
                // 继续执行
                schedulePerformWorkUntilDeadline();
            } else {
                scheduleHostCallback = null;
            }
        }
    }
}

function cancelCallback(task) {
    task.callback = null;
}

export {
    scheduleCallback as unstable_scheduleCallback,
    shouldYieldToHost as unstable_shouldYield,
    cancelCallback as unstable_cancelCallback,
    NoPriority,
    ImmediatePriority as unstable_ImmediatePriority,
    UserBlockingPriority as unstable_UserBlockingPriority,
    NormalPriority as unstable_NormalPriority,
    LowPriority as unstable_LowPriority,
    IdlePriority as unstable_IdlePriority,
    getCurrentTime as now,
}